package com.csust.automaticteach.controller;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.csust.automaticteach.annotation.AuthCheck;
import com.csust.automaticteach.app.common.ExerciseEvaluateApp;
import com.csust.automaticteach.common.BaseResponse;
import com.csust.automaticteach.common.ResultUtils;
import com.csust.automaticteach.constant.UserAuthConstant;
import com.csust.automaticteach.exception.BusinessException;
import com.csust.automaticteach.exception.ErrorCode;
import com.csust.automaticteach.exception.ThrowUtils;
import com.csust.automaticteach.model.dto.exercisessets.ExercisesSetsQueryRequest;
import com.csust.automaticteach.model.dto.exercisessubmit.JudgeExercisesRequest;
import com.csust.automaticteach.model.dto.generateexam.AddGenerateExamRequest;
import com.csust.automaticteach.model.dto.generatequestion.AddGenerateQuestionRequest;
import com.csust.automaticteach.model.entity.*;
import com.csust.automaticteach.model.enums.GenerateTypeEnum;
import com.csust.automaticteach.model.vo.exercises.ExercisesVO;
import com.csust.automaticteach.model.vo.exercises.GenExercisesVO;
import com.csust.automaticteach.service.ExercisesSetsService;
import com.csust.automaticteach.service.ExercisesSubmitService;
import com.csust.automaticteach.service.UserService;
import com.csust.automaticteach.utils.template.generate.ExamGenerator;
import com.csust.automaticteach.utils.template.generate.QuestionGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName GenerateExercisesController
 * @Description  生成习题相关接口
 * @Author钰玟
 * @Date 2025/7/24 下午2:16
 * @Version 1.0
 **/
@RestController
@RequestMapping("/generateExercises")
public class GenerateExercisesController {
    @Resource
    private ExercisesSetsService exercisesSetsService;
    @Resource
    private UserService userService;
    @Resource
    private QuestionGenerator questionGenerator;
    @Resource
    private ExamGenerator examGenerator;
    @Resource
    private ExercisesSubmitService exercisesSubmitService;

    //region examination 测试模块
    /**
     * 生成测试练习  （教师端）
     * @param addGenerateExamRequest 添加测试请求
     * @param request 请求
     * @return List<GenerateExamVO>
     */
    @PostMapping("/exam/add")
    public BaseResponse<GenExercisesVO> generateExam(@RequestBody AddGenerateExamRequest addGenerateExamRequest
            , HttpServletRequest request) {
        //1.校验参数
        ThrowUtils.throwIf(addGenerateExamRequest == null, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        //2.生成可行性校验
        String reference= addGenerateExamRequest.getReference();
        Long generateTeachId = addGenerateExamRequest.getGenerateTeachId();
        String knowledgePoint = addGenerateExamRequest.getKnowledgePoint();
        // true 表示“只有一个字段非空”
        boolean onlyOne = StrUtil.isBlank(reference) ^ ObjUtil.isNotNull(generateTeachId) ^
                StrUtil.isNotBlank(knowledgePoint);
        ThrowUtils.throwIf(!onlyOne, ErrorCode.PARAMS_ERROR);
        //3. 模板方法 生成测试
        GenExercisesVO genExercisesVO = examGenerator.generate(addGenerateExamRequest, loginUser);
        return ResultUtils.success(genExercisesVO);
    }

    /**
     * 获取某个详情的生成测试
     *
     * @param setId 对应id
     * @return
     */
    @GetMapping("/exam/get")
    public BaseResponse<List<ExercisesVO>> getGenerateExam(@RequestParam("setId") long setId) {
        //1. 参数校验
        ThrowUtils.throwIf(setId <= 0, ErrorCode.PARAMS_ERROR);
        //2. 获取题目
        List<ExercisesVO> exerciseVOList = exercisesSetsService.getExerciseVOList(setId);
        //3. 返回结果
        return ResultUtils.success(exerciseVOList);
    }
    /**
     * 分页获取题目列表
     *
     * @param exercisesSetsQueryRequest 查询条件
     * @param request                   请求
     * @return Page<ExercisesSets>
     */
    @PostMapping("/exam/list/page")
    @AuthCheck(mustRole = UserAuthConstant.TEACHER_ROLE)
    public BaseResponse<Page<ExercisesSets>> listGenerateExamByPage(@RequestBody ExercisesSetsQueryRequest exercisesSetsQueryRequest
            , HttpServletRequest request) {
        //1. 参数校验
        ThrowUtils.throwIf(exercisesSetsQueryRequest == null, ErrorCode.PARAMS_ERROR);
        //2. 设置查询类型
        Integer setType = GenerateTypeEnum.EXAMINATION.getValue();
        exercisesSetsQueryRequest.setSetType(setType);
        //3. 分页查询
        Page<ExercisesSets> exercisesSetsPage = exercisesSetsService.listExercisesSetByPage(exercisesSetsQueryRequest, request);
        //4.返回结果
        return ResultUtils.success(exercisesSetsPage);
    }

    /**
     * 删除生成的测试 exam
     * @param setId 套题id
     * @return 删除结果
     */
    @PostMapping("/exam/delete")
    public BaseResponse<Boolean> deleteGenerateExam(@RequestParam("setId") Long setId) {
        //1.校验参数
        ThrowUtils.throwIf(setId == null || setId <= 0, ErrorCode.PARAMS_ERROR);
        //2.删除
        Boolean b = exercisesSetsService.deleteExercisesSet(setId);
        //3.返回结果
        ThrowUtils.throwIf(!b, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }
    //endregion

    //region  question 模块

    /**
     * 生成练习题目 （学生端）
     *
     * @param addGenerateQuestionRequest 添加题目请求
     * @param request                    请求
     * @return List<GenerateQuestionVO>
     */
    @PostMapping("/question/add")
    public BaseResponse<GenExercisesVO> generateQuestion(@RequestBody AddGenerateQuestionRequest addGenerateQuestionRequest
            , HttpServletRequest request) {
        //校验参数
        ThrowUtils.throwIf(addGenerateQuestionRequest == null, ErrorCode.PARAMS_ERROR);
        //获取用户
        User loginUser = userService.getLoginUser(request);
        GenExercisesVO genExercisesVO = questionGenerator.generate(addGenerateQuestionRequest, loginUser);
        return ResultUtils.success(genExercisesVO);
    }

    /**
     * 分页获取题目列表
     *
     * @param exercisesSetsQueryRequest 查询条件
     * @param request                   请求
     * @return Page<GenerateQuestionPageVO>
     */
    @PostMapping("/question/list/page")
    public BaseResponse<Page<ExercisesSets>> listGenerateQuestionByPage(@RequestBody ExercisesSetsQueryRequest exercisesSetsQueryRequest
            , HttpServletRequest request) {
        //1. 参数校验
        ThrowUtils.throwIf(exercisesSetsQueryRequest == null, ErrorCode.PARAMS_ERROR);
        //2. 设置查询类型
        Integer setType = GenerateTypeEnum.QUESTION.getValue();
        exercisesSetsQueryRequest.setSetType(setType);
        //3. 分页查询
        Page<ExercisesSets> exercisesSetsPage = exercisesSetsService.listExercisesSetByPage(exercisesSetsQueryRequest, request);
        //4.返回结果
        return ResultUtils.success(exercisesSetsPage);
    }

    /**
     * 获取题目
     *
     * @param setId 题目 <组> id
     * @return GenerateQuestionVO
     */
    @GetMapping("/question/get")
    public BaseResponse<List<ExercisesVO>> getGenerateQuestion(@RequestParam("setId") long setId) {
        //1. 参数校验
        ThrowUtils.throwIf(setId <= 0, ErrorCode.PARAMS_ERROR);
        //2. 获取题目
        List<ExercisesVO> exerciseVOList = exercisesSetsService.getExerciseVOList(setId);
        //3. 返回结果
        return ResultUtils.success(exerciseVOList);
    }

    /**
     * 删除生成题目<组>
     *
     * @param setId 题目<组>id
     * @return 删除结果
     */
    @PostMapping("/question/delete")
    public BaseResponse<Boolean> deleteGenerateQuestion(@RequestParam("setId") Long setId) {
        //1.校验参数
        ThrowUtils.throwIf(setId == null || setId <= 0, ErrorCode.PARAMS_ERROR);
        //2.删除
        Boolean b = exercisesSetsService.deleteExercisesSet(setId);
        //3.返回结果
        ThrowUtils.throwIf(!b, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }
    //endregion


    //region  judge 模块
    @PostMapping("/judge")
    public BaseResponse<List<ExerciseEvaluateApp.judgeResponse>> judgeExercises(@RequestBody JudgeExercisesRequest judgeExercisesRequest
            ,HttpServletRequest request) {
        //1.校验参数
        ThrowUtils.throwIf(judgeExercisesRequest == null, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        //2.调用判题服务
        List<ExerciseEvaluateApp.judgeResponse> judgeResponses = exercisesSubmitService.judgeExercises(judgeExercisesRequest,loginUser);
        //3. 更新数据库
        Thread.ofVirtual().start(() -> {
            //使用虚拟线程更新数据库
            judgeResponses.forEach(exercisesSubmitService::updateSubmitResult);
        });
        //4.返回结果
        return ResultUtils.success(judgeResponses);
    }

    /**
     * 实时显示 判题结果 通过 sse 技术 ，而sse只支持 get 请求
     * 所以使用 @requestParam 获取参数
     * @param id 套题的id 用于查询套题的相关信息
     * @param ansMap 用户的答案  {id1:ans1,id2:ans2}
     * @return SseEmitter
     * 示例数据
     * <p>
     *     {
     *         "id": 1958176359149121537,
     *         "isCorrect": true,
     *         "improvement": "答案完全正确！你已经准确掌握 Spring AI 中 EmbeddingModel 的核心作用，理解其在自然语言处理中的关键地位。继续精进的方向：
     *         ① 深入了解不同 Embedding 模型（如 Word2Vec、BERT）在 Spring AI 中的具体实现方式；
     *         ② 动手实践使用 EmbeddingModel 对文本进行向量化，并结合相似度算法实现简单的语义检索功能；
     *         ③ 探索 Spring AI 中 EmbeddingModel 与其他组件（如 ChatModel、PromptTemplate）的协同使用方式。
     *         继续保持这种扎实的学习状态，未来在 AI 工程化方向会有很大发展空间！"
     *     }
     * </p>
     */
    @GetMapping(value = "/judge/stream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter  judgeExercisesStream(@RequestParam("id") Long id,
                                            @RequestParam("ansMap") String ansMap
                                            ,HttpServletRequest request) {
        //1.校验参数
        // 1.1  id
        if(id == null || id <= 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //1.2  ansMap
        if(ansMap == null || StrUtil.isBlank(ansMap)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //2. 封装为 JudgeExercisesRequest
        JudgeExercisesRequest judgeExercisesRequest = new JudgeExercisesRequest();
        judgeExercisesRequest.setId(id);
        HashMap<Long, String> userAns = JSONUtil.toBean(ansMap, new TypeReference<HashMap<Long, String>>() {}, true);
        judgeExercisesRequest.setUserAns(userAns);//变成map
        //3. 处理流式相应
        //3.1  处理预处理 前置准备
        SseEmitter emitter = new SseEmitter(0L);
        StringBuilder contentBuilder = new StringBuilder();
        AtomicInteger flag = new AtomicInteger(0);
        User loginUser = userService.getLoginUser(request);
        //3.2  调用service 执行判题 并实时获取判题结果
        try {
            Flux<String> stringFlux = exercisesSubmitService.judgeExercisesByStream(judgeExercisesRequest,loginUser);
            stringFlux.publishOn(Schedulers.boundedElastic())
                    .map(message -> message.replaceAll("\\s", ""))//移除字符串中的所有空白字符（包括空格、换行符等）。
                    .filter(StrUtil::isNotBlank)//过滤掉空字符串或只包含空白字符的字符串。
                    .flatMap(message -> {//将每个字符串转换为字符流。
                        // 将字符串转换为 List<Character>
                        List<Character> charList = new ArrayList<>();
                        for (char c : message.toCharArray()) {
                            charList.add(c);
                        }
                        return Flux.fromIterable(charList);
                    })
                    .doOnNext(c -> {
                        {
                            // 识别第一个 [ 表示开始 AI 传输 json 数据，打开 flag 开始拼接 json 数组
                            if (c == '{') {
                                flag.addAndGet(1);
                            }
                            if (flag.get() > 0) {
                                contentBuilder.append(c);
                            }
                            if (c == '}') {
                                flag.addAndGet(-1);
                                if (flag.get() == 0) {
                                    // 累积单套题目满足 json 格式后，sse 推送至前端
                                    // sse 需要压缩成当行 json，sse 无法识别换行
                                    try {
                                        String chunkResponse = JSONUtil.toJsonStr(contentBuilder.toString()) ;
                                        emitter.send(chunkResponse);
                                        ObjectMapper om = new ObjectMapper();
                                        ExerciseEvaluateApp.judgeResponse judgeResponse = om.readValue(chunkResponse, ExerciseEvaluateApp.judgeResponse.class);
                                        //更新数据库
                                        exercisesSubmitService.updateSubmitResult(judgeResponse);
                                    } catch (IOException e) {
                                        emitter.completeWithError(e);
                                        throw new BusinessException(ErrorCode.SYSTEM_ERROR,"题目生成失败");
                                    }
                                    // 清空 StringBuilder
                                    contentBuilder.setLength(0);
                                }
                            }
                        }
    //                    emitter.send(SseEmitter.event().name("done").data("").build());
                    }).doOnComplete(()->{
                        try {
                            emitter.send(SseEmitter.event().name("done").data("").build());
                        } catch (IOException e) {
                            emitter.completeWithError(e);
                            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"题目生成失败");
                        }
                        emitter.complete();
                    }).subscribe();
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"题目生成失败");
        }
        return emitter;

    }
    //endregion
}
